/**
*  \file timex.c
*
*  \brief Date and time conversion.
*
*  \author   Michael J. Hohmann <Michael.Hohmann@linde-mh.de>
*
*/

/*===============================================[ private includes  ]================================================*/
#include "timex.h"

/*===============================================[ private defines   ]================================================*/

/** Days of a year */
#define DAYS_A_YEAR		   365U

/** Days of a leap year. */
#define DAYS_A_LEAP_YEAR   366U

/** Days of year 0 (seen from 1970 ) */
#define DAYS_OF_YEAR_0	   DAYS_A_YEAR

/** Days of year 1 (seen from 1970 ) */
#define DAYS_OF_YEAR_1     (DAYS_OF_YEAR_0 + DAYS_A_YEAR)

/** Days of year 2 (seen from 1970 ) */
#define DAYS_OF_YEAR_2	   (DAYS_OF_YEAR_1 + DAYS_A_LEAP_YEAR)

/* Number of days of a leap period (for years). */
#define DAYS_OF_FOUR_YEARS ( 3*DAYS_A_YEAR + DAYS_A_LEAP_YEAR )

/* Seconds of the year 2100-01-01 */ 
#define SECONDS_OF_YEAR_2100 4102444799U
/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/** Number of days for each month of a non leap year.*/
const static U16 monthly_days_of_year[]  	 = {0,31,59,90,120,151,181,212,243,273,304,334,365};

/** Number of days for each month of a leap year.*/
const static U16 monthly_days_of_leap_year[] = {0,31,60,91,121,152,182,213,244,274,305,335,366};

/*===============================================[ public variables  ]================================================*/

/*===============================================[ private functions ]================================================*/
STATIC_INLINE BO is_leap_year( U16 year );

/*====================================================================================================================*/


RC timex_TimeToDate( timex_t time, struct timex_date *pDate )
{
   U16 days_since;
   U16 days_left;

   U16 const *pMonthly_days_of_year=&monthly_days_of_year[11]; /* tricky - will be later set to leap year if necessary.*/

   if ( pDate == NIL )
   {
      return RC_ERROR_NULL; /* unacceptable null pointer */
   }

   if (time > SECONDS_OF_YEAR_2100 )
   {
   	  return RC_ERROR_RANGE; /* out of range */
   }

   pDate->tm_isdst  = -1; /* set daylight saving time to undefined */

   pDate->tm_sec 	= time % 60;
   time          	= time / 60;

   pDate->tm_min 	= time % 60;
   time          	= time / 60;

   pDate->tm_hour 	= time % 24;
   days_since 		= time / 24;

   pDate->tm_wday 	= (days_since + 3) % 7;

   /* First calculate number of leap periodes, and perform a binary seach for the remaining	days */
   pDate->tm_year 	= days_since / DAYS_OF_FOUR_YEARS;
   days_left		= days_since % DAYS_OF_FOUR_YEARS;
   pDate->tm_year   = pDate->tm_year * 4;
   pDate->tm_year   = pDate->tm_year + 70;
   if( days_left < DAYS_OF_YEAR_1 )
   {
      if( days_left < DAYS_OF_YEAR_0 )
      {

      }
      else
      {
         days_left -=DAYS_OF_YEAR_0;
         pDate->tm_year+=1;
      }
   }
   else
   {
      if( days_left < DAYS_OF_YEAR_2 )
      {
         pMonthly_days_of_year= &monthly_days_of_leap_year[11]; /* set to leapyear */
         days_left -= DAYS_OF_YEAR_1;
         pDate->tm_year+=2;
      }
      else
      {
         days_left -= DAYS_OF_YEAR_2;
         pDate->tm_year += 3;
      }
   }

   pDate->tm_yday = days_left;

   /* Linear search to find the current month */
   for( pDate->tm_mon=11; *pMonthly_days_of_year > days_left; pDate->tm_mon--, pMonthly_days_of_year-- );

   pDate->tm_mday= days_left - *pMonthly_days_of_year + 1 ;

   return 0;
}


RC timex_DateToTime( struct timex_date const *pDate, timex_t *p_time )
{
   U16 i;
   U32 temp_data = 0;
   U16 year;

   if ( pDate == NIL   || p_time == NIL)
   {
     return RC_ERROR_NULL; /* unacceptable null pointer */
   }

   *p_time=0;

   year  = 1900 + pDate->tm_year;

   for(i = 1970; i < year; i++)
   {
      if( is_leap_year(i) ) /* leap year */
      {
         *p_time += DAYS_A_LEAP_YEAR;
      }
      else
      {
         *p_time += DAYS_A_YEAR;
      }
   }

   temp_data = pDate->tm_mon;

   for(i = 0; i < temp_data; i++)
   {
      if(i == 3 || i == 5 || i == 8 || i == 10)
      {
         *p_time += 30;
      }
      else if( i == 1)
      {
         if( is_leap_year(year) ) /* leap year */
            *p_time += 29;
         else
            *p_time += 28;
      }
      else
      {
         *p_time += 31;
      }
   }

   *p_time += pDate->tm_mday - 1;
   *p_time *= 24;

   *p_time += pDate->tm_hour;
   *p_time *= 60;

   *p_time += pDate->tm_min;
   *p_time *= 60;

   *p_time += pDate->tm_sec;

    return 0;

}

STATIC_INLINE BO is_leap_year( U16 year )
{
    return (((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0);
}

/***********************************************************************************************************************
*                        Copyright 2007-2018 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
